package map.core;

import ucar.ma2.Array;
import ucar.ma2.DataType;
import ucar.ma2.Range;
import ucar.nc2.*;
import ucar.nc2.write.NetcdfFormatWriter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

  public class NetCDFSourceExtractor {

    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ucar.nc2.write.NetcdfCopier.class);
    private static final long maxSize = 50 * 1000 * 1000; // 50 Mbytes
    private static boolean debug, debugWrite;
      private final List<NetCDFLayer> layers;

      private   NetcdfFormatWriter.Builder writerb;
    private boolean extended;

    NetcdfFile fileIn;
    NetCDFSource source;


    public NetCDFSourceExtractor(NetCDFSource source,String out) {
        this.writerb = NetcdfFormatWriter.createNewNetcdf3(out);
        this.source = source;
        this.fileIn = source.getNcfile();
        this.layers = source.listAllLayer();

    }





    private void copyDimension(Variable oldVariable, IntRange range, Group.Builder group){
        String name = oldVariable.getFullName();//这个获取的是外面定义的名称
        if(group.findDimension(name).isEmpty()){

            String diemName =oldVariable.getDimension(0).getName();//这个是获取维度里面的名称

            Dimension.Builder newDimension = Dimension.builder().setName(diemName)
                    .setLength(range.getWidth()) ;
            group.addDimension(newDimension.build());


            Variable.Builder newVariable =Variable.builder()
                    .setParentGroupBuilder(group)
                     .setName(name)
                    .setDimensionsByName(diemName)
                    .setDataType(DataType.DOUBLE);//oldVariable.getDataType() //定义固定的类型 不然会报类型转换错误
            group.addVariable(newVariable);

         }
    }
      private void copyDimensiontime(Variable oldVariable, IntRange range, Group.Builder group){
          String name = oldVariable.getFullName();
          try {
              Array read = oldVariable.read();
              long size = read.getSize();

              if(group.findDimension(name).isEmpty()){
                  Dimension.Builder newDimension = Dimension.builder().setName(name)
                          .setLength((int) size) ;
                  group.addDimension(newDimension.build());


                  Variable.Builder newVariable =Variable.builder()
                          .setParentGroupBuilder(group)
                          .setName(name)
                          .setDimensionsByName(name)
                          .setDataType(DataType.DOUBLE);//oldVariable.getDataType() //定义固定的类型 不然会报类型转换错误
                  group.addVariable(newVariable);

              }
          } catch (IOException e) {
              throw new RuntimeException(e);
          }
      }
    private void copyVariable(Variable oldVariable, Group.Builder group){
        String name = oldVariable.getFullName();
        if(group.findVariableLocal(name).isEmpty()){

            Variable.Builder newVariable =Variable.builder()
                    .setParentGroupBuilder(group)
                    .setName(name)
                    .setDimensions(oldVariable.getDimensions())
                    .setDataType(oldVariable.getDataType());
            group.addVariable(newVariable);

         }
    }

    private void copyDimensionData(Variable oldVariable, IntRange range, NetcdfFormatWriter ncwriter){
        try {
            List<Range> readRanges = new ArrayList<>();
            readRanges.add(new Range(range.getMin(),range.getMax()-1));
            Array data = oldVariable.read(readRanges);
            ncwriter.write(oldVariable.getFullName(),data);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


    private void copyLayerData(NetCDFLayer layer, NetcdfFormatWriter ncwriter, IntRange lonRange, IntRange latRange, IntRange sliceRange) {
        try{
            IntRange[] intRanges = new IntRange[layer.variable.getDimensions().size()];

            intRanges[layer.lonVariableOrder] = lonRange;
            intRanges[layer.latVariableOrder] = latRange;
             if(layer.sliceVariableOrder >= 0){
                 intRanges[layer.sliceVariableOrder] =sliceRange;
             }


            List<Range> ranges = new ArrayList<>();
            for (IntRange intRange : intRanges) {
                if(intRange == null){//防止带z字段为null
                    ranges.add(new Range(0,0));
                }else{
                    ranges.add(new Range(intRange.getMin(),intRange.getMax()-1));
                }
            }
            Variable oldVariable = layer.variable;

            /*Variable variable = layer.netcdfFile.findVariable(name);
            String unitsString = variable.getUnitsString();
            oldVariable.addAttribute(new Attribute("units",unitsString));*/
            Array data = oldVariable.read(ranges);
            for (int i = 0; i < data.getSize(); i++) {
                //System.out.println(data.getDouble(i));
                if(data.getDouble(i) == 9.999999933815813E36){//有的nc数据中会出现NaN的数据 该数据读取出来就是这个，判断这个给改回成NaN格式
                    data.setDouble(i,Double.NaN);
                }
            }
            ncwriter.write(oldVariable.getFullName(),data);

        }
        catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    private IntRange calSliceRange(Variable variable, DoubleRange valueRange){
          if(variable == null){
              return null;
          }
          if(valueRange == null){
              return new IntRange(0,variable.getShape(0)-1);
          }
          try{
              double[] data = toDoubleArray(variable.read());
              int minIndex =getIndex(data,0, data.length-1, valueRange.getMin());
              int maxIndex =getIndex(data,0, data.length-1, valueRange.getMax());

              return new IntRange(minIndex,maxIndex);

          }
          catch (Exception e){
              throw new RuntimeException(e);
          }
    }

    private double[] toDoubleArray(Array array){
        int size = (int) array.getSize();
        double[] rs = new double[size];
        for(int i = 0 ;i < size ;  i ++){
            rs[i] = array.getDouble(i);
        }
        return  rs;
    }

    private int getIndex(double[] nums,int lo,int hi,double target){

        if(lo == hi) {
            return lo;
        }

        int middle = 0;
        while (lo < hi) {
            middle = (lo + hi) / 2;
            if (target == nums[middle]) {
                return middle;
            } else if (Math.abs(target - nums[middle]) > Math.abs(target - nums[middle + 1])) {
                lo = middle + 1;
            }else{
                hi = middle - 1;
            }
        }

        return Math.abs(target - nums[middle]) > Math.abs(target - nums[middle + 1]) ? (middle + 1) : middle;

    }


      /**
       *
       * @param bounds  必填
       * @param timeRange 可为空
       */
    public void extract(LonLatBounds bounds, DoubleRange timeRange){
          if(!this.layers.isEmpty()){
              NetCDFLayer layer = this.layers.get(0);
              IntRange lonRange = calSliceRange(layer.lonVariable,bounds.getLonRange());
              IntRange latRange = calSliceRange(layer.latVariable,bounds.getLatRange());
              IntRange sliceRange = calSliceRange(layer.sliceVariable,timeRange);
              this.extract(lonRange,latRange,sliceRange);
          }else {
              System.out.println("无可用图层，忽略后续操作");
          }
    }

      public void extractzdy(LonLatBounds bounds, DoubleRange timeRange,String name){
          if(!this.layers.isEmpty()){
              for (int i = 0; i < this.layers.size(); i++) {
                  if(name.equals(this.layers.get(i).getBaseInfo().getName())){
                      NetCDFLayer layer = this.layers.get(i);
                      IntRange lonRange = calSliceRange(layer.lonVariable,bounds.getLonRange());
                      IntRange latRange = calSliceRange(layer.latVariable,bounds.getLatRange());
                      IntRange sliceRange = calSliceRange(layer.sliceVariable,timeRange);
                      IntRange zRange = new IntRange(0,1);
                      this.extractzdy(lonRange,latRange,sliceRange,name,zRange);
                  }
              }
          }else {
              System.out.println("无可用图层，忽略后续操作");
          }
      }

    private void extract(IntRange lonRange, IntRange latRange, IntRange sliceRange)   {


        int width = lonRange.getWidth();
        int width1 = latRange.getWidth();
        if(lonRange.getWidth() < 2 || latRange.getWidth() < 2){
            return;
        }

        if(sliceRange != null && sliceRange.getWidth() < 1){
            return;
        }

        this.fileIn = source.getNcfile();

        Group oldGroup = fileIn.getRootGroup();

        //create new Group
        Group.Builder newGroup = Group.builder().setName(oldGroup.getShortName());


        //copy Dimension & variable Base Info
        for (NetCDFLayer layer : layers) {
            if (layer.lonVariable != null) {
                copyDimension(layer.lonVariable, lonRange, newGroup);
            }

            if (layer.latVariable != null) {
                copyDimension(layer.latVariable, latRange, newGroup);
            }

            if (layer.sliceVariable != null) {
                copyDimension(layer.sliceVariable, sliceRange, newGroup);
            }

            copyVariable(layer.variable,newGroup);
        }

        writerb.setRootGroup(newGroup);

        //copy data
        try (NetcdfFormatWriter ncwriter = writerb.build()) {

            for (NetCDFLayer layer : layers) {
                if (layer.lonVariable != null) {
                    copyDimensionData(layer.lonVariable,lonRange,ncwriter);
                 }

                if (layer.latVariable != null) {
                    copyDimensionData(layer.latVariable,latRange,ncwriter);
                }
                if (layer.sliceVariable != null) {
                    copyDimensionData(layer.sliceVariable,sliceRange,ncwriter);
                }
                copyLayerData(layer,ncwriter,lonRange,latRange,sliceRange);

            }

            ncwriter.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

      private void extractzdy(IntRange lonRange, IntRange latRange, IntRange sliceRange,String name,IntRange zRange)   {


          int width = lonRange.getWidth();
          int width1 = latRange.getWidth();
          if(lonRange.getWidth() < 2 || latRange.getWidth() < 2){
              return;
          }

          if(sliceRange != null && sliceRange.getWidth() < 1){
              return;
          }

          this.fileIn = source.getNcfile();

          Group oldGroup = fileIn.getRootGroup();

          //create new Group
          Group.Builder newGroup = Group.builder().setName(oldGroup.getShortName());


          //copy Dimension & variable Base Info
          for (NetCDFLayer layer : layers) {
              if(name.equals(layer.getBaseInfo().getName())){
                  if (layer.lonVariable != null) {
                      copyDimension(layer.lonVariable, lonRange, newGroup);
                  }
                  if (layer.latVariable != null) {
                      copyDimension(layer.latVariable, latRange, newGroup);
                  }
                  if (layer.sliceVariable != null) {
                      copyDimension(layer.sliceVariable, sliceRange, newGroup);

                  }
                  if (layer.zVariable != null) {
                      copyDimension(layer.zVariable, zRange, newGroup);
                  }
                  Variable variable = layer.netcdfFile.findVariable(name);
                  if(variable.getUnitsString()!=null){
                  newGroup.addVariable(variable.toBuilder().addAttribute(new Attribute("units",variable.getUnitsString())));//添加单位
                  //newGroup.addAttribute(new Attribute("units",variable.getUnitsString()));
                  }
                  copyVariable(layer.variable,newGroup);
              }
          }

          writerb.setRootGroup(newGroup);
          //copy data
          try (NetcdfFormatWriter ncwriter = writerb.build()) {
              for (NetCDFLayer layer : layers) {
                      if(name.equals(layer.getBaseInfo().getName())){
                          if (layer.lonVariable != null) {
                              copyDimensionData(layer.lonVariable,lonRange,ncwriter);
                          }

                          if (layer.latVariable != null) {
                              copyDimensionData(layer.latVariable,latRange,ncwriter);
                          }

                          if (layer.sliceVariable != null) {
                              copyDimensionData(layer.sliceVariable,sliceRange,ncwriter);
                          }

                          copyLayerData(layer,ncwriter,lonRange,latRange,sliceRange);

                  }
              }

              ncwriter.flush();
          } catch (IOException e) {
              throw new RuntimeException(e);
          }
      }



}
